// utils/bluetooth.js
const promisify = (fn, args = {}) => {
  return new Promise((resolve, reject) => {
    fn(Object.assign({}, args, {
      success: res => resolve(res),
      fail: err => reject(err)
    }))
  })
}

const Bluetooth = {
  inited: false,
  onDeviceFoundCallback: null,
  onConnectionChangeCallback: null,
  onCharChangeCallback: null,

  // 初始化适配器
  async init() {
    // uni API 会在小程序端映射到 wx.openBluetoothAdapter
    if (this.inited) return
    await promisify(uni.openBluetoothAdapter)
    this.inited = true
    // 监听本地蓝牙适配器状态变化
    uni.onBluetoothAdapterStateChange(res => {
      console.log('adapterStateChange', res)
      if (!res.available) {
        // 蓝牙关闭或不可用时，应提示用户打开
      }
    })
  },

  // 开始发现设备
  async startDiscovery() {
    // allow duplicates to get RSSI updates (微信平台下有 allowDuplicatesKey)
    await promisify(uni.startBluetoothDevicesDiscovery, { allowDuplicatesKey: false })
    // 绑定回调
    uni.onBluetoothDeviceFound(device => {
      // device可能包含advertisData、advertisServiceUUIDs等
      if (this.onDeviceFoundCallback) {
        // 规范字段
        const d = {
          deviceId: device.deviceId || device.deviceId,
          name: device.name || device.localName || device.advertisServiceUUIDs && device.advertisServiceUUIDs[0] || '',
          RSSI: device.RSSI,
          advertisData: device.advertisData || device.advertisServiceUUIDs
        }
        this.onDeviceFoundCallback(d)
      }
    })
  },

  stopDiscovery() {
    return promisify(uni.stopBluetoothDevicesDiscovery)
  },

  onDeviceFound(cb) {
    this.onDeviceFoundCallback = cb
  },

  offDeviceFound(cb) {
    if (this.onDeviceFoundCallback === cb) this.onDeviceFoundCallback = null
  },

  // 建立连接
  createConnection(deviceId) {
    return new Promise((resolve, reject) => {
      uni.createBLEConnection({
        deviceId,
        success: res => resolve(res),
        fail: err => reject(err)
      })
    })
  },

  closeConnection(deviceId) {
    return promisify(uni.closeBLEConnection, { deviceId })
  },

  onConnectionStateChange(cb) {
    // 小程序提供 onBLEConnectionStateChange
    uni.onBLEConnectionStateChange(res => {
      // { deviceId, connected }
      cb && cb(res)
    })
  },

  // 获取服务
  getServices(deviceId) {
    return new Promise((resolve, reject) => {
      uni.getBLEDeviceServices({
        deviceId,
        success: res => resolve(res.services || []),
        fail: err => reject(err)
      })
    })
  },

  // 获取特征
  getCharacteristics(deviceId, serviceId) {
    return new Promise((resolve, reject) => {
      uni.getBLEDeviceCharacteristics({
        deviceId,
        serviceId,
        success: res => {
          // 特征数组，包括 uuid, properties { read/write/notify/indicate }
          resolve(res.characteristics || [])
        },
        fail: err => reject(err)
      })
    })
  },

  // 读特征
  readCharacteristic(deviceId, serviceId, characteristicId) {
    return new Promise((resolve, reject) => {
      uni.readBLECharacteristicValue({
        deviceId,
        serviceId,
        characteristicId,
        success: res => {
          // 读取成功后，微信小程序还会触发 onBLECharacteristicValueChange，某些平台回调在此之前或之后
          // 为统一返回，监听一次 onBLECharacteristicValueChange
          const handler = (data) => {
            if (data.deviceId === deviceId && (data.characteristicId === characteristicId || data.characteristicId === data.characteristicId)) {
              uni.offBLECharacteristicValueChange && uni.offBLECharacteristicValueChange(handler)
              resolve(data.value)
            }
          }
          // 也直接resolve空，以免丢失
          resolve(null)
        },
        fail: err => reject(err)
      })
    })
  },

  // 写特征
  writeCharacteristic(deviceId, serviceId, characteristicId, arrayBuffer) {
    return new Promise((resolve, reject) => {
      uni.writeBLECharacteristicValue({
        deviceId,
        serviceId,
        characteristicId,
        value: arrayBuffer,
        success: res => resolve(res),
        fail: err => reject(err)
      })
    })
  },

  // 开启/关闭通知
  notify(deviceId, serviceId, characteristicId, state = true) {
    return new Promise((resolve, reject) => {
      uni.notifyBLECharacteristicValueChange({
        state,
        deviceId,
        serviceId,
        characteristicId,
        success: res => resolve(res),
        fail: err => reject(err)
      })
    })
  },

  // 监听特征值变化
  onBLECharacteristicValueChange(cb) {
    this.onCharChangeCallback = cb
    uni.onBLECharacteristicValueChange(data => {
      // data.value 是 ArrayBuffer
      cb && cb(data)
    })
  },

  // 工具方法：ArrayBuffer <-> Hex/String
  arrayBufferToHex(buffer) {
    if (!buffer) return ''
    const byteArray = new Uint8Array(buffer)
    const hexArr = []
    for (let i = 0; i < byteArray.length; i++) {
      const hex = byteArray[i].toString(16).padStart(2, '0')
      hexArr.push(hex)
    }
    return hexArr.join('')
  },

  hexToArrayBuffer(hexStr) {
    if (!hexStr) return new ArrayBuffer(0)
    const s = hexStr.replace(/\s+/g, '')
    const len = s.length / 2
    const u8 = new Uint8Array(len)
    for (let i = 0; i < len; i++) {
      u8[i] = parseInt(s.substr(i * 2, 2), 16)
    }
    return u8.buffer
  },

  stringToArrayBuffer(str) {
    // UTF-8 编码
    const encoder = new TextEncoder()
    return encoder.encode(str).buffer
  },

  arrayBufferToString(buffer) {
    if (!buffer) return ''
    try {
      const decoder = new TextDecoder()
      return decoder.decode(new Uint8Array(buffer))
    } catch (err) {
      // fallback
      let arr = new Uint8Array(buffer)
      let str = ''
      for (let i = 0; i < arr.length; i++) str += String.fromCharCode(arr[i])
      return str
    }
  }
}

export default Bluetooth
